package com.demo.common.ratelimit.lock;

import com.demo.common.redis.constant.RedisLuaConstant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.connection.ReturnType;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.TimeoutUtils;
import org.springframework.data.redis.core.types.Expiration;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * Redis分布式锁
 *
 * @author DayDayUp
 * create on 2023-08-24
 **/
@Slf4j
public class RedisLock {
    @Resource
    private RedisTemplate<String,Object> redisTemplate;

    /**
     * 获取分布式锁，原子操作
     * @param lockKey 分布式锁key
     * @param requestId 唯一ID, 可以使用UUID.randomUUID().toString();
     * @param expire 过期时间
     * @param timeUnit 时间单位
     * @return boolean
     */
    public boolean tryLock(String lockKey, String requestId, long expire, TimeUnit timeUnit) {
        try{
            RedisCallback<Boolean> callback = conn -> {
                long millis = TimeoutUtils.toMillis(expire, timeUnit);
                return conn.set(lockKey.getBytes(StandardCharsets.UTF_8), requestId.getBytes(StandardCharsets.UTF_8), Expiration.milliseconds(millis), RedisStringCommands.SetOption.SET_IF_ABSENT);
            };
            return Boolean.TRUE.equals(redisTemplate.execute(callback));
        } catch (Exception e) {
            log.error("redis lock error.", e);
        }
        return false;
    }

    /**
     * 释放锁
     * @param lockKey 分布式锁key
     * @param requestId 唯一ID
     * @return boolean
     */
    public boolean releaseLock(String lockKey, String requestId) {
        RedisCallback<Boolean> callback = conn -> {
            return conn.eval(RedisLuaConstant.RELEASE_LOCK.getBytes(), ReturnType.BOOLEAN ,1, lockKey.getBytes(StandardCharsets.UTF_8), requestId.getBytes(StandardCharsets.UTF_8));
        };
        return Boolean.TRUE.equals(redisTemplate.execute(callback));
    }

    /**
     * 获取Redis锁的value值
     * @param lockKey 分布式锁key
     * @return String
     */
    public String get(String lockKey) {
        try {
            RedisCallback<String> callback = conn -> new String(Objects.requireNonNull(conn.get(lockKey.getBytes())), StandardCharsets.UTF_8);
            return redisTemplate.execute(callback);
        } catch (Exception e) {
            log.error("get redis occurred an exception", e);
        }
        return null;
    }
}
